package EP::Visualization::Grid;

use strict;

use EP::Config;
use EP::Common;
use EP::Visualization::Cell;

use XML::LibXML::SVG;

use GD;
use PDL;
use Data::UUID;

use constant SVG_MARGIN => 5;
use constant TBR_BTN_SIZE => 0; # TO DO (or to remove?)

sub new {
    my $proto = shift;
    my $class = ref($proto) || $proto;
    my $self  = {};
    bless $self, $class;
    $self->initialize(@_) if @_;
    return $self;
}

#############################################
#Initializes an empty grid according to the #
#dimensions passed in the constructor       #
#############################################
sub initialize {
  my $self = shift;
  my ( $x_dim, $y_dim ) = @_;
  $self->{xDim} = $x_dim;
  $self->{yDim} = $y_dim;

  $self->{htbr_buttons} = {};
  $self->{vtbr_buttons} = {};
# TO DO (or to remove?)
#  btn_export_svg => { id     => 'btn_export_svg',
#						icon   => "$EP::Config::h->{HTML_HREF}/static/images/gui_icons/export.png",
#						tip    => 'Export SVG image...',
#						action => ''
#					      },
#			    btn_zoom_in    => { id     => 'btn_zoom_in',
#						icon   => "$EP::Config::h->{HTML_HREF}/static/images/gui_icons/zoom_in.png",
#						tip    => 'Zoom in...',
#						action => "zoomer(2)" },
#			    btn_zoom_out   => { id     => 'btn_zoom_out',
#						icon   => "$EP::Config::h->{HTML_HREF}/static/images/gui_icons/zoom_out.png",
#						tip    => 'Zoom out...',
#						action => "zoomer(0.5)" },
#			    btn_zoom_reset => { id     => 'btn_zoom_reset',
#						icon   => "$EP::Config::h->{HTML_HREF}/static/images/gui_icons/zoom_reset.png",
#						tip    => 'Reset View',
#						action => "reset()" }
#			  };


  push @{$self->{widths}},  30 for 1 .. $x_dim;
  push @{$self->{heights}}, 30 for 1 .. $y_dim;

  return;
}

############################################################
#sums up the height of all the graphics objects present    #
#in the first column of the grid and uses as it's          #
#height                                                    #
############################################################
sub getGridHeight {
  my $self = shift;
  my $realvalue = @_;
  return $self->{gridheight} if !$realvalue;

  my $heights = pdl @{$self->{heights}};
  my $y = sum $heights;

  return $y + 2 * SVG_MARGIN + TBR_BTN_SIZE + 4;
}

###############################################################
#sums up the width of all the graphics objects present in the #
#first row of the grid and uses the as it's width             #
###############################################################
sub getGridWidth {
  my $self = shift;
  my $realvalue = @_;
  return $self->{gridwidth} if !$realvalue;

  my $widths = pdl @{$self->{widths}};
  my $x = sum $widths;

  return $x + 2 * SVG_MARGIN + TBR_BTN_SIZE + 4;
}

sub getHeight {
  my $self = shift;
  return $self->getGridHeight(1);
}

sub getWidth {
  my $self = shift;
  return $self->getGridWidth(1);
}

######################################################################
# fills the entire grid with an array of cells
######################################################################
sub fillCellArray {
  my $self = shift;
  my @cells = @{$_[0]};

  for my $row ( 0 .. $#cells ) {
    for my $col ( 0 .. $#{$cells[0]} ) {
      my $cell = $cells[$row][$col];
      if ( defined $cell ) {
	my $cell_width  = $cell->getWidth ();
	my $cell_height = $cell->getHeight ();

	$self->{cells}{$col}{$row} = $cell;

	$self->{widths}->[$col]  = $cell_width  if $cell_width  > $self->{widths}->[$col];
	$self->{heights}->[$row] = $cell_height if $cell_height > $self->{heights}->[$row];
      }
    }
  }
}

##############################################################
#fill a cell with a graphics object                          #
##############################################################
sub fillCell {
    my $self = shift;
    my ( $x, $y, $width, $height, $obj ) = @_;
    $self->{cells}{$x}{$y} = $obj;
#    $self->{cells}{$x}{$y}->setWidth($width);
#    $self->{cells}{$x}{$y}->setHeight($height);
#    $self->{cells}{$x}{$y}->setGraphicObject($obj);
    return;
}

#############################################################
#to create cell without graphics object                     #
#############################################################
sub fillEmptyCell {
    my $self = shift;
    my ( $x, $y, $width, $height ) = @_;

    $self->{cells}{$x}{$y}->setWidth  ( $width  );
    $self->{cells}{$x}{$y}->setHeight ( $height );

    return;
}

############################################################
#calculates the x position of the upper left corner voor a #
#particular cell                                           #
############################################################
sub getGridX {
  my $self  = shift;
  my ($cellX) = @_;

  my $x = 0;

  for ( my $i = 0 ; $i < $cellX ; $i++ ) {
    my $ith_width = $self->{widths}->[$i];

    $x += $ith_width;
  }

  return $x;
}

############################################################
#calculates the y position of the upper left corner voor a #
#particular cell                                           #
############################################################
sub getGridY {
  my $self  = shift;
  my ($cellY) = @_;

  my $y = 0;

  for ( my $i = 0 ; $i < $cellY ; $i++ ) {
    my $ith_height = $self->{heights}->[$i];

    $y += $ith_height;
  }

  return $y;
}

sub getImageMap {
  my $self = shift;

  return $self->{image_map} if $self->{image_map} ne "<map></map>";
}

# Sub: to_png
#   Renders the entire grid to a PNG image
# Parameters:
#   pngout - output filename for the PNG file
#   width, height - force resize of output PNG to specified w/h
sub to_png {
  my $self = shift;
  my %params = @_;

  my $vw = $self->getGridWidth(1);
  my $vh = $self->getGridHeight(1);

  my $png = new GD::Image ( $vw, $vh );
  my $white = $png->colorAllocate(255,255,255);

  $self->{image_map} = "<map>";

  # here we iterate over all cells in the grid and render the graphics objects inside of them separately
  for ( my $x = 0 ; $x < $self->{xDim} ; $x++ ) {
    for ( my $y = 0 ; $y < $self->{yDim} ; $y++ ) {
      if ( exists $self->{cells}{$x}{$y} ) {
	my $graphic_obj = $self->{cells}{$x}{$y};#->getGraphicObject();
	warn "Rendering " . ref ( $graphic_obj ) . " at $x, $y" if DEBUG;
	if ( $graphic_obj->can ( "to_png" ) ) {
	  $graphic_obj->to_png ( $png,
				 $self->{cells}{$x}{$y}->getHeight(),
				 $self->{cells}{$x}{$y}->getWidth(),
				 $self->getGridX($x),
				 $self->getGridY($y) );

	  # obtain a client-side image map if the object defined one
	  if ( exists $graphic_obj->{image_map} ) {
	    foreach my $elt_def ( @{$graphic_obj->{image_map}} ) {
	      my @coords = split /,/, $elt_def->{coords};
	      $coords[0] += $self->getGridX($x);
	      $coords[1] += $self->getGridY($y);
              if ($elt_def->{shape} eq 'rectangle') {
	      $coords[2] += $self->getGridX($x);
	      $coords[3] += $self->getGridY($y);
              }
	      my $c = join (",",@coords);

	      $self->{image_map} .= "<area shape='$elt_def->{shape}' coords='$c' href='$elt_def->{href}' title='$elt_def->{title}'";
              $self->{image_map} .= " nohref='1'" if $elt_def->{href} eq '#';
              $self->{image_map} .= "></area>";
	    }
	  }
	} else {
	  warn ref($graphic_obj), " couldn't to_png!" if DEBUG;
	}
      }
    }
  }

  $self->{image_map} .= "</map>";

  my $opng;
  warn "Grid wants to go to $vw x $vh!";

  $params{height} = 20000 if $vh > 20000;
  $params{width} = 20000 if $vw > 20000;

  warn "Rescaled the image down..." if $vh > 20000 or $vw > 20000;

  if ( ( exists $params{width} or exists $params{height} ) and ($vw > $params{width} or $vh > $params{height} ) ) {
    $params{width} = 400  if not exists $params{width};
    $params{height} = 600 if not exists $params{height};

    $opng = new GD::Image ( $params{width}, $params{height} );
    $opng->copyResampled ( $png, 0, 0, 0, 0, $params{width}, $params{height}, $vw, $vh );
  } else {
    $opng = $png;
  }

  open PNG, ">$params{pngout}" or die "Couldn't open $params{pngout}: $!";
  print PNG $opng->png(9);
  close PNG;

  if ( $self->{image_map} ne "<map></map>" ) {
    $self->{image_map} =~ s/<map>/<map name="$params{pngout}">/;
    open PNGMAP, ">$params{pngout}.map" or die "Couldn't open $params{pngout}.map: $!";
    print PNGMAP $self->{image_map};
    close PNGMAP;

    $self->{image_map} = "$params{pngout}.map";
  }
}

sub to_svg {
  my $self = shift;
  my %params = @_;

  ( $self->{svg_filename} ) = ( $params{toFile} =~ /$EP::Config::h->{TMP}\/(.*)/ ) if exists $params{toFile};

  my $gw = $self->getGridWidth(1);
  my $gh = $self->getGridHeight(1);

  $params{width} = 800 if not exists $params{width};
  $params{height} = 600 if not exists $params{height};

  $self->{gridwidth} = $params{width};
  $self->{gridheight} = $params{height};

  my $svg_params = { 'xmlns:a3'                => "http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/",
		     'xmlns:xlink'             => "http://www.w3.org/1999/xlink",
		     'xmlns:gui'               => "http://www.kevlindev.com/gui",
		     'a3:scriptImplementation' => "Adobe",
		     preserveAspectRatio       => "xMinYMin",
		     onzoom                    => "on_zoom()",
		     onload                    => "init(evt)",
		     width                     => $params{width},
		     height                    => $params{height},
		     viewBox                   => "0 0 $gw $gh",
                    'text-rendering'           => 'optimizeSpeed',
#                    'shape-rendering'          => 'optimizeSpeed',
                    'image-rendering'          => 'optimizeSpeed',
		     'color-rendering'          => 'optimizeSpeed'
		   } if not exists $params{subgrid};

  my $svg = XML::LibXML::SVG->createSVGDocument ( %$svg_params );

#  $svg->script ( type                      => "text/javascript",
#                 "a3:scriptImplementation" => "Adobe",
#                 "xlink:href" => "$EP::Config::h->{HTML_HREF}/static/js/scrollBars.js" );

  $self->_add_js_to_svg ( $svg, $params{stand_alone} ) if not exists $params{subgrid};
  $self->_add_defs_to_svg ( $svg, $params{stand_alone} ) if not exists $params{subgrid};
#  $self->_add_menu_to_svg ( $svg ) if not exists $params{subgrid};

#   $svg->rect ( id => "inner_grid", x => 1, 'y' => 1, width => $gw - 2, height => $gh - 2, fill => "none", stroke => "black", "stroke-width" => 0.5 ) if not exists $params{subgrid};
  my $inner_svg = $svg->group ( transform => "translate( " . (TBR_BTN_SIZE + 4 + SVG_MARGIN) . "," . (TBR_BTN_SIZE + 4 + SVG_MARGIN) . ")" ) if not exists $params{subgrid};
     $inner_svg = $svg if exists $params{subgrid};

  # here we iterate over all cells in the grid and render the graphics objects inside of them separately
  my $g = $inner_svg;
  for ( my $x = 0 ; $x < $self->{xDim} ; $x++ ) {
    for ( my $y = 0 ; $y < $self->{yDim} ; $y++ ) {
      if ( exists $self->{cells}{$x}{$y} ) {
	my ( $cell_height, $cell_width, $cell_xpos, $cell_ypos ) = ( $self->{cells}{$x}{$y}->getHeight(),
								     $self->{cells}{$x}{$y}->getWidth(),
								     $self->getGridX($x),
								     $self->getGridY($y) );

	if ( ref ( $self->{cells}{$x}{$y} ) eq "EP::Visualization::Grid" ) {
	  $g->rect ( x => $cell_xpos, 'y' => $cell_ypos,
		     height => $cell_height - 2 * SVG_MARGIN - TBR_BTN_SIZE - 4,
		     width  => $cell_width - 2 * SVG_MARGIN - TBR_BTN_SIZE - 4,
		     fill => "none",
		     stroke => 'steelblue',
		     'stroke-width' => 0.1 );

	  my $subgrid = $self->{cells}{$x}{$y}->to_svg ( subgrid => 1, width => $cell_width, height => $cell_height );
	  my $group = $g->group ( transform => "translate($cell_xpos,$cell_ypos)" );  #translates the heatmap according to it's position in the grid
	  $group->addChild ( $subgrid );
	} else {
	  $g->rect ( x => $cell_xpos, 'y' => $cell_ypos,
		     height => $cell_height,
		     width  => $cell_width,
		     fill => "none",
		     stroke => 'steelblue',
		     'stroke-width' => 0.1 );

	  $self->_register_buttons (  $self->{cells}{$x}{$y} );
	  $g = $self->{cells}{$x}{$y}->to_svg ( $g, $cell_height, $cell_width, $cell_xpos, $cell_ypos );
	  $self->_render_cell_toolbar ( $g, $x, $y );
	}
      }
    }
  }

  $self->_setup_toolbars_to_svg ( $svg, $params{stand_alone} ) if not exists $params{subgrid};

  if (not exists $params{subgrid}) {
    $g = $svg->group ( id => 'tooltip' );
    $g->rect ( id => 'ttr', 'x' => 0, 'y' => 0, rx => 3, ry => 3, width => '100', height => 16, style => 'visibility: hidden' );
    $g->text ( id => 'ttt', 'x' => 0, 'y' => 0, style => 'visibility: hidden' )->cdata ( 'dynamic text' );

    $svg->group ( id => 'messages', transform => 'translate(100,50)' );

    my ( $svg_filename ) = ($params{toFile} =~ /$EP::Config::h->{TMP}\/(.*)/) if exists $params{toFile};
    $svg->script (  type => "text/javascript", "a3:scriptImplementation" => "Adobe" )->cdata ( "var svg_filename = '$EP::Config::h->{HTML_HREF}/tmp/$svg_filename';" ) if exists $params{toFile};
  }

  warn "Writing to $params{toFile}";
  if ( exists $params{toFile} ) {
    warn "Compressing..." if $params{toFile} =~ /svgz$/;
    $svg->ownerDocument->setCompression ( 8 ) if $params{toFile} =~ /svgz$/;
    $svg->toFile ( $params{toFile}, 1 );
    return OK;
  } if ( exists $params{subgrid} ) {
    return $svg;
  } else {
    return $svg->toString(1);
  }
}

sub _render_cell_toolbar {
  my ( $self ) = shift;
  my ( $svg, $x, $y ) = @_;

  my $obj = $self->{cells}{$x}{$y};

  my $buttons;
  my $tbr_buttons = $obj->tbr_buttons();
  return if not $tbr_buttons;

  $buttons->{$_->{id}} = $_ foreach $tbr_buttons;
  return if not $buttons;

  my $objx = $self->getGridX($x);
  my $objy = $self->getGridY($y);

  my $ug = new Data::UUID;
  my $tbr_id = $ug->create_str();

# my $tbr_marker = $svg->use ( 'xlink:href' => '#tbr_marker', 'x' => $objx, 'y' => $objy,
#			       onmouseover => "document.getElementById ('$tbr_id').getStyle().setProperty('display','visible')" );

  my $tbr = $self->_draw_toolbar ( $svg, $buttons );
  $tbr->setAttribute ( "id", $tbr_id );
  $tbr->setAttribute ( "transform", "translate($objx,$objy)" );
  $tbr->setAttribute ( "style", "display:none" );
  $tbr->setAttribute ( "onmouseout", "document.getElementById ('$tbr_id').getStyle().setProperty('display','none')" );
}

sub _register_buttons {
  my $self = shift;
  my ( $obj ) = @_;

  defined $_ ? $self->{vtbr_buttons}->{$_->{id}} = $_ : 0 foreach $obj->vtbr_buttons();
  defined $_ ? $self->{htbr_buttons}->{$_->{id}} = $_ : 0 foreach $obj->htbr_buttons();
}

sub _draw_toolbar {
  my $self = shift;
  my ($svg, $buttons) = @_;

  my $tbr_inner_size = TBR_BTN_SIZE + 3;

  my $tbgp = $svg->group ();
  $tbgp->rect ( x => 0, 'y' => 0, width => ( scalar( keys ( %$buttons ) ) * (TBR_BTN_SIZE + 2) - 1 ),
		height => $tbr_inner_size, fill => "lightgray", stroke => "black", "stroke-width" => 0.5 );

  my $hpos = 1;
  foreach my $btn ( values %$buttons ) {
    my $btngp = $tbgp->group ( id => $btn->{id}, transform=>"translate($hpos,1)", style => { 'stroke-width' => 2 },
			       onmousedown => "document.getElementById('$btn->{id}').getStyle().setProperty('stroke','red')",
			       onmouseup   => "document.getElementById('$btn->{id}').getStyle().setProperty('stroke','none')",
			       onmouseover => "ShowTooltip('$btn->{tip}', evt);evt.propagate='stop';",
			       onmouseout  => "HideTooltip();evt.stopPropagation();",
			       onclick     => "$btn->{action}" );
#   $btngp->use ( 'y' => 0, 'x' => 0, 'xlink:href' => '#button', style => "filter:url(#button_filter)" );
    $btngp->anchor->image ( x => 0, 'y' => 0, width => TBR_BTN_SIZE, height => TBR_BTN_SIZE, 'xlink:href' => $btn->{icon} );
    $hpos += TBR_BTN_SIZE;
  }

  return $tbgp;
}

sub _setup_toolbars_to_svg {
  my $self = shift;
  my ($svg, $standalone) = @_;

  my ( $real_width, $real_height )  = ( $self->getGridWidth(1), $self->getGridHeight(1) );

  my $tbr_outer_size = TBR_BTN_SIZE + 4;
  my $tbr_inner_size = TBR_BTN_SIZE + 3;

  my $vtgp = $svg->group ( id => 'h_tbr', transform => "translate(1,$tbr_outer_size)" );
  $vtgp->rect ( x => 0, 'y' => 0, width => "$tbr_inner_size", height => '100%', fill => "lightgray", stroke => "black", "stroke-width" => 0.5 );
#( $real_height - $tbr_outer_size - 1 )

  my $htgp = $svg->group ( id => 'v_tbr', transform => "translate($tbr_outer_size,1)" );
  $htgp->rect ( x => 0, 'y' => 0, width => '100%', height => "$tbr_inner_size", fill => "lightgray", stroke => "black", "stroke-width" => 0.5 );
#( $real_width - $tbr_outer_size - 1 )

  my $hpos = 1;
  foreach my $btn ( values %{$self->{htbr_buttons}} ) {
    next if not defined $btn;

    my $btngp = $htgp->group ( id => $btn->{id}, transform=>"translate($hpos,1)", style => { 'stroke-width' => 2 },
			       onmousedown => "document.getElementById('$btn->{id}').getStyle().setProperty('stroke','red')",
			       onmouseup   => "document.getElementById('$btn->{id}').getStyle().setProperty('stroke','none')",
			       onmouseover => "ShowTooltip('$btn->{tip}', evt)",
			       onmouseout  => "HideTooltip()",
			       onclick     => "$btn->{action}" );
#   $btngp->use ( 'y' => 0, 'x' => 0, 'xlink:href' => '#button', style => "filter:url(#button_filter)" );
    $btngp->anchor->image ( x => 0, 'y' => 0, width => TBR_BTN_SIZE, height => TBR_BTN_SIZE, 'xlink:href' => $btn->{icon} );
    $hpos += TBR_BTN_SIZE + 1;
  }

  my $vpos = 1;
  foreach my $btn ( values %{$self->{vtbr_buttons}} ) {
    next if not defined $btn;

    my $btngp = $vtgp->group ( id => $btn->{id}, transform=>"translate(1,$vpos)", style => { 'stroke-width' => 2 },
			       onmousedown => "document.getElementById('$btn->{id}').getStyle().setProperty('stroke','red')",
			       onmouseup   => "document.getElementById('$btn->{id}').getStyle().setProperty('stroke','none')",
			       onmouseover => "ShowTooltip('$btn->{tip}', evt)",
			       onmouseout  => "HideTooltip()",
			       onclick     => "$btn->{action}" );
    if ( $btn->{id} eq "btn_export_svg" ) {
      if ( exists $self->{svg_filename} ) {
#	$btngp->use ( 'y' => 0, 'x' => 0, 'xlink:href' => '#button', style => "filter:url(#button_filter)" );
	$btngp->anchor ( 'xlink:href' => "$EP::Config::h->{CORE_HREF}/download_file.pl?filename=$self->{svg_filename}" )->image ( x => 0, 'y' => 0, width => TBR_BTN_SIZE, height => TBR_BTN_SIZE, 'xlink:href' => $btn->{icon} );
      } else {
	$vpos -= TBR_BTN_SIZE + 1;
      }
    } else {
#     $btngp->use ( 'y' => 0, 'x' => 0, 'xlink:href' => '#button', style => "filter:url(#button_filter)" );
      $btngp->anchor->image ( x => 0, 'y' => 0, width => TBR_BTN_SIZE, height => TBR_BTN_SIZE, 'xlink:href' => $btn->{icon} );
    }
    $vpos += TBR_BTN_SIZE + 1;
  }

#  # button
#  my $btn1 = $htgp->group ( id => "but1", transform=>"translate(1,1)", style => { 'stroke-width' => 0.5 },
#			    onmousedown => "document.getElementById('but1').getStyle().setProperty('stroke','red')",
#			    onmouseup   => "document.getElementById('but1').getStyle().setProperty('stroke','none')",
#			    onclick     => "alert('but1')" );
#  $btn1->use ( 'y' => 0, 'x' => 0, 'xlink:href' => '#button', style => "filter:url(#button_filter)" );
#  $btn1->anchor->text ( x => 6, 'y' => 15, )->cdata ( 'Y' );

#  my $btn2 = $htgp->group ( id => "but2", transform=>"translate(22,1)", style => { 'stroke-width' => 2 },
#			    onmousedown => "document.getElementById('but2').getStyle().setProperty('stroke','red')",
#			    onmouseup   => "document.getElementById('but2').getStyle().setProperty('stroke','none')",
#			    onmouseover => "ShowTooltip('Save clusters...', evt)",
#			    onmouseout  => "HideTooltip()",
#			    onclick     => "alert('but2')" );
#  $btn2->use ( 'y' => 0, 'x' => 0, 'xlink:href' => '#button', style => "filter:url(#button_filter)" );
#  $btn2->anchor->image ( x => 0, 'y' => 0, width => TBR_BTN_SIZE, height => TBR_BTN_SIZE, 'xlink:href' => 'save2.png' );

#  my $btn3 = $htgp->group ( id => "but3", transform=>"translate(43,1)", , style => { 'stroke-width' => 0.5 },
#			    onmousedown => "document.getElementById('but3').getStyle().setProperty('stroke','red')",
#			    onmouseup   => "document.getElementById('but3').getStyle().setProperty('stroke','none')",
#			    onclick     => "alert('but3')" )->anchor;
#  $btn3->use ( 'y' => 0, 'x' => 0, 'xlink:href' => '#button', style => "filter:url(#button_filter)" );
#  $btn3->text ( x => 6, 'y' => 15, )->cdata ( 'Z' );
}


sub _add_defs_to_svg {
  my $self = shift;
  my ($svg) = @_;

  my $defs = $svg->defs ();

  my $filter = $defs->filter ( id => "button_filter" );

  $filter->feGaussianBlur ( in => 'SourceAlpha', stdDeviation => 1, result => "blur" );
  $filter->feOffset       ( in => 'blur', dx => 1, dy => 1, result => "offsetBlur" );

  my $sl = $filter->feSpecularLighting ( in => 'blur', surfaceScale => 5, specularConstant => 1, specularExponent => 10, style => { 'lighting-color' => 'white' }, result => "specOut" );
  $sl->fePointLight ( 'x' => -5000, 'y' => -10000, 'z' => 20000 );

  $filter->feComposite ( in => "specOut", in2 => "SourceAlpha", operator=>"in", result => "specOut" );
  $filter->feComposite ( in => "SourceGraphic", in2 => "specOut", operator=>"arithmetic", k1 => 0, k2=> 1, k3=>1, k4=>0, result => "litPaint" );

  my $mr = $filter->feMerge();
  $mr->feMergeNode ( in => "offsetBlur" );
  $mr->feMergeNode ( in => "litPaint" );

  my $btn_gp = $defs->group ( id => "button" );
  $btn_gp->group()->rect ( width => TBR_BTN_SIZE, height => TBR_BTN_SIZE, fill => "red" );

  my $tbr_marker = $defs->group ( id => "tbr_marker" );
  $tbr_marker->polyline ( points => "0 0 5 5 10 0", style => "fill:red" );

  $defs->style(type => 'text/css' )->cdata ( <<__STYLE__ );
      polyline {
        fill: none;
        stroke: black;
        stroke-width: 1
      }

     circle.orangecircle { fill: orange }
     circle.purplecircle { fill: purple }
__STYLE__
}

sub _add_js_to_svg {
  my $self = shift;
  my ($svg, $standalone) = @_;

  $svg->script (  type => "text/javascript", "a3:scriptImplementation" => "Adobe" )->cdata ( "var html_href = '$EP::Config::h->{HTML_HREF}';" );

  $svg->script ( type                      => "text/javascript",
		 "a3:scriptImplementation" => "Adobe",
		 "xlink:href" => "$EP::Config::h->{HTML_HREF}/static/js/ButtonTags.js" );

  $svg->script ( type                      => "text/javascript",
		 "a3:scriptImplementation" => "Adobe",
		 "xlink:href" => "$EP::Config::h->{HTML_HREF}/static/js/pick_color.js" );

  $svg->script ( type                      => "text/javascript",
		 "a3:scriptImplementation" => "Adobe",
		 "xlink:href" => "$EP::Config::h->{HTML_HREF}/static/js/alert.js" );

  $svg->script ( type                      => "text/javascript",
		 "a3:scriptImplementation" => "Adobe",
		 "xlink:href"              =>
		  "$EP::Config::h->{HTML_HREF}/static/js/AntiZoomAndPan.js" );

  $svg->script ( type                      => "text/javascript",
		 "a3:scriptImplementation" => "Adobe",
		 "xlink:href"              =>
		 "$EP::Config::h->{HTML_HREF}/static/js/ToolTip2.js" );

  $svg->script ( type                      => "text/javascript",
		 "a3:scriptImplementation" => "Adobe",
		 "xlink:href"              =>
		 "$EP::Config::h->{HTML_HREF}/static/js/VennDiagram.js" );

  $svg->script ( type                      => "text/javascript",
		 "a3:scriptImplementation" => "Adobe",
		 "xlink:href" => "$EP::Config::h->{HTML_HREF}/static/js/ep_svg.js" );
}

sub _add_menu_to_svg {
  my $self = shift;
  my ($svg) = @_;

  my $defs = $svg->defs ( id => 'menu' );
  my $menu = $defs->tag ( 'menu', id => 'epng_menu' );#, onload => 'GetPosition(evt)' );
  $menu->tag ('header')->cdata ( 'EP:NG' );
  $menu->tag ('item', onactivate => 'zoomer(2)'  )->cdata ( "Zoom &In" );
  $menu->tag ('item', onactivate => 'zoomer(0.5)')->cdata ( "Zoom &Out" );
  $menu->tag ('item', onactivate => 'reset()'    )->cdata ( "Original &View" );

  $menu->tag ('separator');
  $menu->tag ('item', action     => 'SaveAs'     )->cdata ( "Save &As" );
  $menu->tag ('item', action     => 'ViewSource' )->cdata ( "View Sourc&e..." );
  $menu->tag ('item', action     => 'Find'       )->cdata ( "&Find..." );
  $menu->tag ('item', action     => 'About'      )->cdata ( "&About..." );

  my $cluster_menu = $defs->tag ( 'menu', id=>'cluster_menu' );
  $cluster_menu->tag ( 'item', onactivate => 'save_cluster_as_dataset()' )->cdata ( "&Save cluster as dataset..." );
  $cluster_menu->tag ( 'item', onactivate => 'go_annot_cluster()'        )->cdata ( "&GO annotate cluster..." );
}

sub add_zoom_and_pan_gui {
    my $self = shift;
    my ($svg) = @_;

    my $g = $svg->group ( id => "zoom_and_pan" );
    $g->rect(
        x      => "100",
        y      => "0",
        width  => "100%",
        height => "60",
        fill   => "white"
    );

    my $gg = $g->group ( transform => "translate(100,0)" );
    $gg->rect(
        x      => "7",
        y      => "7",
        width  => "118",
        height => "27",
        fill   => "white",
        style  => "stroke: black; stroke-width:1; opacity:1"
    );
    $gg->tag(
        "button",
        -namespace => "gui",
        x          => "10",
        y          => "10",
        width      => "20",
        height     => "20",
        label      => "+",
        callback   => "zoomer(2)"
    );
    $gg->tag(
        "button",
        -namespace => "gui",
        x          => "40",
        y          => "10",
        width      => "20",
        height     => "20",
        label      => "-",
        callback   => "zoomer(0.5)"
    );

#    $gg->tag(
#        "button",
#        -namespace => "gui",
#        x          => "70",
#        y          => "10",
#        width      => "20",
#        height     => "20",
#        label      => "L",
#        callback   => "pan(-1,0)"
#    );
#    $gg->tag(
#        "button",
#        -namespace => "gui",
#        x          => "100",
#        y          => "10",
#        width      => "20",
#        height     => "20",
#        label      => "R",
#        callback   => "pan(1,0)"
#    );
#    $gg->tag(
#        "button",
#        -namespace => "gui",
#        x          => "130",
#        y          => "10",
#        width      => "20",
#        height     => "20",
#        label      => "D",
#        callback   => "pan(0,-1)"
#    );
#    $gg->tag(
#        "button",
#        -namespace => "gui",
#        x          => "160",
#        y          => "10",
#        width      => "20",
#        height     => "20",
#        label      => "U",
#        callback   => "pan(0,1)"
#    );
    $gg->tag(
        "button",
        -namespace => "gui",
        x          => "70",
        y          => "10",
        width      => "50",
        height     => "20",
        label      => "RESET",
        callback   => "reset()"
    );

#     $g->rect(
#         x      => "0",
#         y      => "40",
#         width  => "100%",
#         height => "1",
#         fill   => "none",
#         style  => "stroke: black; stroke-width: 1"
#     );
}

1;
